package org.misalen.web.service.pay.wechatpay;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.SAXReader;
import org.misalen.utils.TextUtil;
import org.misalen.web.service.file.impl.OssFileImpl;
import org.springframework.stereotype.Service;

import net.sf.json.JSONObject;

/**
 * 微信支付相关实现
 * 
 * @author misaya
 *
 */
@Service
public class WechatPayImpl {
	String appid = null;
	String mchid = null;
	String key = null;
	String unifiedorderUrl = null;
	static Properties p = new Properties();
	static {
		InputStream in = OssFileImpl.class.getClassLoader().getResourceAsStream("pro.properties");
		try {
			p.load(in);
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public WechatPayImpl() {
		appid = p.getProperty("pay.wechatpay.appid");
		mchid = p.getProperty("pay.wechatpay.mchid");
		key = p.getProperty("pay.wechatpay.key");
		unifiedorderUrl = p.getProperty("pay.wechatpay.unifiedorderUrl");
	}

	/**
	 * 
	 * @param body
	 *            商品或支付单简要描述
	 * @param out_trade_no
	 *            商户系统内部的订单号 32个字符内 可包含字母
	 * @param total_fee
	 *            总金额 单位是分 不能是小数
	 * @param attach
	 *            附加数据 原样返回
	 * @param spbill_create_ip
	 *            APP和网页支付提交用户端ip
	 * @param notifyUrl 
	 * 
	 * @return 拼接生成订单请求参数
	 */
	public String orderedParam(String body, String out_trade_no, String total_fee, String attach,
			String spbill_create_ip, String notifyUrl) {
		// 添加数据
		String nonceStr = TextUtil.getNonceStr(32);
		List<NameValuePair> params = new LinkedList<NameValuePair>();
		params.add(new BasicNameValuePair("appid", appid));
		params.add(new BasicNameValuePair("body", body));
		params.add(new BasicNameValuePair("fee_type", "CNY"));
		params.add(new BasicNameValuePair("mch_id", mchid));
		params.add(new BasicNameValuePair("nonce_str", nonceStr));
		params.add(new BasicNameValuePair("notify_url", notifyUrl));
		params.add(new BasicNameValuePair("out_trade_no", out_trade_no));
		params.add(new BasicNameValuePair("spbill_create_ip", spbill_create_ip));
		params.add(new BasicNameValuePair("total_fee", total_fee));
		params.add(new BasicNameValuePair("trade_type", "APP"));
		params.add(new BasicNameValuePair("attach", attach));
		String sign = getSign(params, key);
		params.add(new BasicNameValuePair("sign", sign));
		return listPair2Xml(params);
	}

	/**
	 * 请求微信预支付订单
	 * 
	 * @param orderedParam
	 *            获得的预支付参数
	 * 
	 * @return {nonce_str=g3BKe36Esf6HxGQh, appid=wx8311da27d579c426,
	 *         sign=A672152DF9142CEEB5795FBE11A80260, trade_type=APP,
	 *         return_msg=OK, result_code=SUCCESS, mch_id=1262045401,
	 *         return_code=SUCCESS,
	 *         prepay_id=wx201605181049383009a2e9560130430369}
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> unifiedorder(String orderedParam) throws Exception {
		orderedParam = new String(orderedParam.getBytes("UTF-8"), "ISO8859-1");
		SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
			// 信任所有
			@Override
			public boolean isTrusted(java.security.cert.X509Certificate[] chain, String authType)
					throws java.security.cert.CertificateException {
				return false;
			}
		}).build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
		CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
		HttpPost httpPost = new HttpPost();
		// 发送数据获取返回值
		httpPost.setURI(new URI(unifiedorderUrl));
		StringEntity se = new StringEntity(orderedParam);
		httpPost.setEntity(se);
		CloseableHttpResponse closeableHttpResponse = httpClient.execute(httpPost);
		HttpEntity entity = closeableHttpResponse.getEntity();
		InputStream inputStream = entity.getContent();
		SAXReader reader = new SAXReader();
		Document document = reader.read(inputStream);
		Element element = document.getRootElement();
		inputStream.close();
		return (Map<String, Object>) xml2map(element);

	}

	/**
	 * 获取客户端所需参数
	 * 
	 * @param map
	 *            {nonce_str=g3BKe36Esf6HxGQh, appid=wx8311da27d579c426,
	 *            sign=A672152DF9142CEEB5795FBE11A80260, trade_type=APP,
	 *            return_msg=OK, result_code=SUCCESS, mch_id=1262045401,
	 *            return_code=SUCCESS,
	 *            prepay_id=wx201605181049383009a2e9560130430369}
	 * @return
	 */
	public JSONObject clientParam(Map<String, Object> map) {
		List<NameValuePair> params = new LinkedList<NameValuePair>();
		params.add(new BasicNameValuePair("appid", map.get("appid").toString()));
		params.add(new BasicNameValuePair("timestamp", String.valueOf(System.currentTimeMillis() / 1000)));
		params.add(new BasicNameValuePair("noncestr", TextUtil.getNonceStr(32)));
		params.add(new BasicNameValuePair("package", "Sign=WXPay"));
		params.add(new BasicNameValuePair("partnerid", map.get("mch_id").toString()));
		params.add(new BasicNameValuePair("prepayid", map.get("prepay_id").toString()));
		String sign = getSign(params, key);
		JSONObject jsonObject = new JSONObject();
		for (int i = 0; i < params.size(); i++) {
			jsonObject.put(params.get(i).getName(), params.get(i).getValue());
		}
		jsonObject.put("sign", sign);
		return jsonObject;
	}

	/**
	 * 验证签名
	 * 
	 * @param map
	 *            参数
	 * @return
	 */
	public boolean clickSign(Map<String, String> map) {
		List<NameValuePair> params = new LinkedList<NameValuePair>();
		Set<String> set = map.keySet();
		Iterator<String> it = set.iterator();
		String respSign = "";
		while (it.hasNext()) {
			String key = it.next();
			Object value = map.get(key);
			if (key.equals("sign")) {
				respSign = value.toString();
			} else if (value != null) {
				params.add(new BasicNameValuePair(key, value.toString()));
			}
		}
		String sign = getSign(params, key);
		if (respSign.equals(sign)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 
	 * 转xml
	 * 
	 * @param params
	 * @return
	 */
	private String listPair2Xml(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();
		sb.append("<xml>");
		for (int i = 0; i < params.size(); i++) {
			sb.append("<" + params.get(i).getName() + ">");
			sb.append(params.get(i).getValue());
			sb.append("</" + params.get(i).getName() + ">");
		}
		sb.append("</xml>");
		return sb.toString();
	}

	/**
	 * 生成Sign
	 * 
	 * @param params
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private static String getSign(List<NameValuePair> params, String mch_api_secret) {
		Collections.sort(params, new Comparator<NameValuePair>() {
			@Override
			public int compare(NameValuePair o1, NameValuePair o2) {
				if (o1 == null) {
					return 1;
				}
				if (o2 == null) {
					return -1;
				}
				return o1.getName().compareTo(o2.getName());
			}
		});
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < params.size(); i++) {
			if (!TextUtil.isNullOrEmpty(params.get(i).getValue())) {
				sb.append(params.get(i).getName());
				sb.append("=");
				sb.append(params.get(i).getValue());
				sb.append("&");
			}
		}
		sb.append("key=");
		sb.append(mch_api_secret);
		String packageSign = TextUtil.MD5(sb.toString());
		return packageSign;
	}

	@SuppressWarnings("unchecked")
	private Object xml2map(Element element) {
		Map<String, Object> map = new HashMap<String, Object>();
		List<Element> elements = element.elements();
		if (elements.size() == 0) {
			map.put(element.getName(), element.getText());
			if (!element.isRootElement()) {
				return element.getText();
			}
		} else if (elements.size() == 1) {
			map.put(elements.get(0).getName(), xml2map(elements.get(0)));
		} else if (elements.size() > 1) {
			Map<String, Element> tempMap = new HashMap<String, Element>();
			for (Element ele : elements) {
				tempMap.put(ele.getName(), ele);
			}
			Set<String> keySet = tempMap.keySet();
			for (String string : keySet) {
				Namespace namespace = tempMap.get(string).getNamespace();
				List<Element> elements2 = element.elements(new QName(string, namespace));
				if (elements2.size() > 1) {
					List<Object> list = new ArrayList<Object>();
					for (Element ele : elements2) {
						list.add(xml2map(ele));
					}
					map.put(string, list);
				} else {
					map.put(string, xml2map(elements2.get(0)));
				}
			}
		}

		return map;
	}

}
